#include "TilemapView.h"
#include <QFile>
#include <QJsonDocument>
#include <QPixmap>
#include <QPainter>
#include <QPainterPath>
#include <QJsonArray>
#include <QRegExp>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QDebug>
#include <math.h>
#include <QNetworkReply>
#include <QEventLoop>
#include "elementmap.h"
#include <cmath>

#define VIEW_SIZE 768   //在此设置TilemapView的大小（正方形）

TilemapView::TilemapView(QWidget *parent)
    : QLabel{parent}, cache(50) //在此设置缓存大小
{
    this->setFixedSize(QSize(VIEW_SIZE, VIEW_SIZE));
    is_connect = false;
    this->installEventFilter(this);
    //加载资源图片
    robot_png.load(":/res/robot.png");
    point_png.load(":/res/normal_point.png");
    two_way_png.load(":/res/dir_two_way.png");
    forward_png.load(":/res/dir_forward.png");
    backward_png.load(":/res/dir_backward.png");
    //http manager
    manager = new QNetworkAccessManager(this);
    //image layer
    layer0 = new QLabel(this);
    layer1 = new QLabel(this);
    layer2 = new QLabel(this);
    layer0->setGeometry(0,0,VIEW_SIZE,VIEW_SIZE);
    layer1->setGeometry(0,0,VIEW_SIZE,VIEW_SIZE);
    layer2->setGeometry(0,0,VIEW_SIZE,VIEW_SIZE);
    connect(&cache,&MapCache::map_update,this,&TilemapView::render);
}

void TilemapView::init(QString host, QString port, QString map_name)
{
    is_connect = true;
    point_set.clear();
    line_set.clear();
    cache.clear();
    //同步处理，可以开启一个局部的事件循环，等待响应，不会阻塞线程
    QNetworkReply *reply1 = manager->get(QNetworkRequest(QUrl("http://"+host+":"+port+"/v1/map/file/"+map_name+"/shapefile/map.json")));
    QEventLoop loop1;
    connect(reply1, &QNetworkReply::finished, &loop1, &QEventLoop::quit);
    loop1.exec();
    QNetworkReply *reply2 = manager->get(QNetworkRequest(QUrl("http://"+host+":"+port+"/v1/map/file/"+map_name+"/tilemap/config.json")));
    QEventLoop loop2;
    connect(reply2, &QNetworkReply::finished, &loop2, &QEventLoop::quit);
    loop2.exec();
    //处理reply信息
    //请求路网数据
    readMapJson(reply1);
    //请求瓦片配置数据
    readConfJson(reply2);
    //其他初始化
    ElementMap::setHost(host);
    ElementMap::setPort(port);
    ElementMap::setMapName(map_name);
    left_top.id_x = 0;
    left_top.id_y = 0;
    left_top.pos_x = 0;
    left_top.pos_y = 0;
    right_bottom.id_x = (VIEW_SIZE-1)/256;
    right_bottom.id_y = (VIEW_SIZE-1)/256;
    right_bottom.pos_x = (VIEW_SIZE-1)%256;
    right_bottom.pos_y = (VIEW_SIZE-1)%256;
    robot_x = 0;
    robot_y = 0;
    robot_yaw = 0;
    origin_x = jroot["OriginX"].toDouble();
    origin_y = jroot["OriginY"].toDouble();
    max_layer = jroot["LayerCount"].toInt()-1;
    now_layer = 0;
    setLayer(now_layer);
    render();
}

void TilemapView::render()
{
    if(is_connect==false) return;
    QPixmap map((right_bottom.id_y-left_top.id_y+1)*256, (right_bottom.id_x-left_top.id_x+1)*256);
    map.fill(QColor(240,240,240)); //设置背景颜色
    QPainter p(&map);
    int compansate = std::pow(2,now_layer+1)-layer_map_row;
    for(int i = left_top.id_x; i <= right_bottom.id_x; i++) {
        for(int j = left_top.id_y; j <= right_bottom.id_y; j++) {
            QPixmap temp;
            if(i<0||i>=layer_map_row||j<0||j>=layer_map_col){
                //边界外用空白图填充
                if(!cache.find(-1, 0, 0)) {
                    cache.push(-1, 0, 0);
                }
            }
            else if(!cache.find(now_layer, i + compansate, j)) {  //若找到会将该图放到缓存最前面
                cache.push(now_layer, i + compansate, j);  //若没找到将需要的图推入缓存（缓存最前面）
            }
            temp = cache.get(); //拿到缓存最前面的图
            p.drawPixmap((j-left_top.id_y)*256,(i-left_top.id_x)*256,256,256,temp);
        }
    }
    render_map = map.copy(left_top.pos_x, left_top.pos_y,
             (right_bottom.id_y-left_top.id_y)*256-left_top.pos_x+right_bottom.pos_x+1,
             (right_bottom.id_x-left_top.id_x)*256-left_top.pos_y+right_bottom.pos_y+1).scaled(QSize(width(), height()));
    renderLines();
    renderPoints();
    layer0->setPixmap(render_map);
    renderRobot();
    renderPointcloud();
}

void TilemapView::renderPoints()
{
    QPainter p(&render_map);
    const QVector point_set_temp = point_set;
    for(const Point& point : point_set_temp) {
        p.drawPixmap(xCoordinateToPixel(point.x)-7, yCoordinateToPixel(point.y)-7, point_png);  //点图片大小为16，所以这里减7，使图片中心对准
    }
}

void TilemapView::renderLines()
{
    QPainter p(&render_map);
    p.setPen(QPen(QColor(255,152,0),8));
    //抗锯齿
    p.setRenderHint(QPainter::Antialiasing);
    p.setRenderHint(QPainter::SmoothPixmapTransform);
    //遍历路网直线
    const QVector line_set_temp = line_set;
    QPainterPath path;
    QVector<QVector<double>> lines_position;
    for(const Line& line : line_set_temp) {
        QVector<double> temp(5);
        //计算直线当前像素位置
        temp[0] = xCoordinateToPixel(line.start_point.x);
        temp[1] = yCoordinateToPixel(line.start_point.y);
        temp[2] = xCoordinateToPixel(line.end_point.x);
        temp[3] = yCoordinateToPixel(line.end_point.y);
        temp[4] = line.direct;
        lines_position.push_back(temp);
        path.moveTo(temp[0],temp[1]);
        path.lineTo(temp[2],temp[3]);
    }
    p.drawPath(path);
    for(const QVector<double>& v:lines_position){
        //确定路线方向
        QPixmap direct_png;
        if(v[4] >= 4 && (int)v[4] % 4) {
            direct_png = two_way_png;
        }else if(v[4] >= 4) {
            direct_png = forward_png;
        }else if(v[4] !=0 ){
            direct_png = backward_png;
        }else{
            continue;
        }
        //画路线方向箭头
        double rotation = std::atan2(v[3]-v[1],v[2]-v[0])*57.29578;     //180°÷π=57.29578°
        p.translate((v[0]+v[2])/2,(v[1]+v[3])/2);
        p.rotate(rotation);
        p.drawPixmap(-direct_png.width()/2,-direct_png.height()/2,direct_png);
        p.rotate(-rotation);
        p.translate(-(v[0]+v[2])/2,-(v[1]+v[3])/2);
    }
}

void TilemapView::renderRobot()
{
    QPixmap robot(VIEW_SIZE, VIEW_SIZE);
    robot.fill(Qt::transparent);
    QPainter p(&robot);
    //画轨迹
    if(!track.empty()) {
        p.setRenderHint(QPainter::Antialiasing);
        p.setPen(QPen(QColor(100,100,100),4));
        QPainterPath path;
        path.moveTo(xCoordinateToPixel(track[0].x),yCoordinateToPixel(track[0].y));
        for(int i=1;i<track.size();i++){
            if(now_idx < now_idx_array[i] && now_idx >= now_idx_array[i-1]) {
                double x = xCoordinateToPixel(track[i-1].x+(track[i].x-track[i-1].x)*(now_idx-now_idx_array[i-1])/(now_idx_array[i]-now_idx_array[i-1]));
                double y = yCoordinateToPixel(track[i-1].y+(track[i].y-track[i-1].y)*(now_idx-now_idx_array[i-1])/(now_idx_array[i]-now_idx_array[i-1]));
                path.lineTo(x,y);
                p.drawPath(path);
                p.setPen(QPen(QColor(0,0,255),4));
                path.clear();
                path.moveTo(x,y);
                path.lineTo(xCoordinateToPixel(track[i].x),yCoordinateToPixel(track[i].y));
            }
            path.lineTo(xCoordinateToPixel(track[i].x),yCoordinateToPixel(track[i].y));
        }
        p.drawPath(path);
    }
    p.setRenderHint(QPainter::SmoothPixmapTransform);
    p.translate(xCoordinateToPixel(robot_x),yCoordinateToPixel(robot_y));
    p.rotate(-robot_yaw*57.29578+180);   //图片机器人方向与x轴方向相反，所以加180°
    p.drawPixmap(-26,-26,robot_png);    //robot_png的大小为52，所以这里减26，使图片中心对准
    layer2->setPixmap(robot);
}

void TilemapView::setLayer(int layer)
{
    now_layer = layer;
    rate = 0;
    now_resolution = jroot["Layers"].toArray().at(now_layer)["Resolution"].toDouble();
    layer_map_col = jroot["Layers"].toArray().at(now_layer)["ColCount"].toInt();
    layer_map_row = jroot["Layers"].toArray().at(now_layer)["RowCount"].toInt();
}

void TilemapView::mousePressEvent(QMouseEvent* event)
{
    mouse_last_x = event->x();
    mouse_last_y = event->y();
}

void TilemapView::mouseMoveEvent(QMouseEvent* event)
{
    int delta_x = mouse_last_x - event->x();
    int delta_y = mouse_last_y - event->y();
    //更改左上角和右下角在层图中的像素位置，得到平移效果
    left_top.posXAdd(delta_x);
    left_top.posYAdd(delta_y);
    right_bottom.posXAdd(delta_x);
    right_bottom.posYAdd(delta_y);
    mouse_last_x = event->x();
    mouse_last_y = event->y();
    render();
}

void TilemapView::wheelEvent(QWheelEvent * event)
{
    rate += event->angleDelta().y()>0?1:-1;
    int max_rate = VIEW_SIZE / 128;
    if(rate==max_rate+1 && now_layer!=max_layer) {
        int last_layer_map_row = layer_map_row;
        setLayer(now_layer+1);
        left_top.posXSet((left_top.id_y*256+left_top.pos_x)*2);
        left_top.posYSet(layer_map_row*256-(last_layer_map_row*256-left_top.id_x*256-left_top.pos_y)*2);
        right_bottom.posXSet((right_bottom.id_y*256+right_bottom.pos_x)*2+1);
        right_bottom.posYSet(layer_map_row*256-(last_layer_map_row*256-right_bottom.id_x*256-right_bottom.pos_y)*2+1);
        render();
        return;
    }else if(rate==max_rate+1) {
        rate = max_rate;
        return;
    }
    if(rate==-max_rate-1 && now_layer!=0){
        int last_layer_map_row = layer_map_row;
        setLayer(now_layer-1);
        left_top.posXSet((left_top.id_y*256+left_top.pos_x)/2);
        left_top.posYSet(layer_map_row*256-(last_layer_map_row*256-left_top.id_x*256-left_top.pos_y)/2);
        right_bottom.posXSet(left_top.id_y*256+left_top.pos_x+VIEW_SIZE-1);
        right_bottom.posYSet(left_top.id_x*256+left_top.pos_y+VIEW_SIZE-1);
        render();
        return;
    }else if(rate==-max_rate-1) {
        rate = -max_rate;
        return;
    }
    int delta;
    if(rate>0) {
        delta = event->angleDelta().y()>0?32:-32;
    }else if(rate<0) {
        delta = event->angleDelta().y()>0?64:-64;
    }else {
        delta = event->angleDelta().y()>0?64:-32;
    }
    left_top.posXAdd(delta);
    left_top.posYAdd(delta);
    right_bottom.posXAdd(-delta);
    right_bottom.posYAdd(-delta);
    render();
}

bool TilemapView::eventFilter(QObject *obj, QEvent *event)
{
    if(!is_connect) {
        if(event->type()==QEvent::MouseMove||
                event->type()==QEvent::MouseButtonPress||
                event->type()==QEvent::Wheel) {
            return true;
        }
    }
    return QLabel::eventFilter(obj,event);
}

Corner Corner::posXAdd(int x)
{
    pos_x += x;
    if(pos_x>255) {
        id_y += pos_x / 256;
        pos_x %= 256;
    }else if(pos_x<0){
        id_y += pos_x / 256 - 1;
        pos_x = pos_x % 256 + 256;
    }
    return *this;
}

Corner Corner::posYAdd(int y)
{
    pos_y += y;
    if(pos_y>255) {
        id_x += pos_y / 256;
        pos_y %= 256;
    }else if(pos_y<0){
        id_x += pos_y / 256 - 1;
        pos_y = pos_y % 256 + 256;
    }
    return *this;
}

Corner Corner::posXSet(int x)
{
    id_y = 0;
    pos_x = 0;
    posXAdd(x);
    return *this;
}

Corner Corner::posYSet(int y)
{
    id_x = 0;
    pos_y = 0;
    posYAdd(y);
    return *this;
}

//高频率事件
void TilemapView::setRobotPose(double x, double y, double yaw, int idx)
{
    robot_x = x;
    robot_y = y;
    robot_yaw = yaw;
    now_idx = idx;
    renderRobot();
}

double TilemapView::xCoordinateToPixel(double coordinate_x)
{
    if(rate>=0) {
        return ((coordinate_x-origin_x)/now_resolution-left_top.id_y*256-left_top.pos_x-1)*VIEW_SIZE/(VIEW_SIZE-rate*64);
    }else {
        return ((coordinate_x-origin_x)/now_resolution-left_top.id_y*256-left_top.pos_x-1)*VIEW_SIZE/(VIEW_SIZE-rate*128);
    }
}

double TilemapView::yCoordinateToPixel(double coordinate_y)
{
    if(rate>=0) {
        return (layer_map_row*256-(coordinate_y-origin_y)/now_resolution-left_top.id_x*256-left_top.pos_y-1)*VIEW_SIZE/(VIEW_SIZE-rate*64);
    }else {
        return (layer_map_row*256-(coordinate_y-origin_y)/now_resolution-left_top.id_x*256-left_top.pos_y-1)*VIEW_SIZE/(VIEW_SIZE-rate*128);
    }
}

void TilemapView::readMapJson(QNetworkReply * reply)
{
    if(reply->error()!=QNetworkReply::NoError){
        //处理中的错误信息
        qDebug()<<"reply error:"<<reply->errorString();
    }else{
        QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
        const QJsonObject& map_json_root = doc.object();
        const QJsonArray& features = map_json_root["features"].toArray();
        for(const QJsonValue& i : features) {
            if(i["geometry"]["type"].toString()=="Point"){
                point_set.push_back(Point(i["geometry"]["coordinates"][0].toDouble(),
                        i["geometry"]["coordinates"][1].toDouble(),
                        i["properties"]["id"].toString()));
            }
        }
        for(const QJsonValue& i : features) {
            if(i["geometry"]["type"].toString()=="LineString") {
                line_set.push_back(Line(findPoint(i["properties"]["startid"].toString()),
                                        findPoint(i["properties"]["endid"].toString()),
                                        i["properties"]["direct"].toInt() - 100));
            }else if(i["geometry"]["type"].toString()=="MultiPoint"){
                QVector<Point> control_points;
                const QJsonArray coordinates = i["geometry"]["coordinates"].toArray();
                control_points.push_back(findPoint(i["properties"]["startid"].toString()));
                for(int j = 1; j < coordinates.size() - 1; j++){
                    control_points.push_back(Point(coordinates[j][0].toDouble(),coordinates[j][1].toDouble()));
                }
                control_points.push_back(findPoint(i["properties"]["endid"].toString()));
                int index = 0;
                Point last_point;
                QVector<Point> bezier_points= genBezierPointsByControlPoints(control_points, 0.05);
                for(const Point& now_point : bezier_points){
                    if(index!=0){
                        if(index == bezier_points.size()/2){
                            line_set.push_back(Line(last_point, now_point, i["properties"]["direct"].toInt() - 100));
                        }else {
                            line_set.push_back(Line(last_point, now_point, 0));
                        }
                    }
                    last_point = now_point;
                    index++;
                }
            }
        }
    }
    reply->deleteLater();
}

void TilemapView::readConfJson(QNetworkReply * reply)
{
    if(reply->error()!=QNetworkReply::NoError){
        //处理中的错误信息
        qDebug()<<"reply error:"<<reply->errorString();
    }else{
        QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
        jroot = doc.object();
    }
    reply->deleteLater();
}

int TilemapView::factorial(int num)
{
    if(num <= 1) {
        return 1;
    }else {
        return num * factorial(num - 1);
    }
}

QVector<Point> TilemapView::genBezierPointsByControlPoints(QVector<Point> control_points, double step)
{
    QVector<Point> bezier_points;
    int n = control_points.size() - 1;
    double x;
    double y;
    for(double i = 0; i < 1; i += step){
        x = 0;
        y = 0;
        int idx = 0;
        for(Point control_point : control_points) {
            if(idx) {
                x += factorial(n) / factorial(idx) / factorial(n-idx) * control_point.x * std::pow(1-i,n-idx) * std::pow(i,idx);
                y += factorial(n) / factorial(idx) / factorial(n-idx) * control_point.y * std::pow(1-i,n-idx) * std::pow(i,idx);
            }else {
                x += control_point.x * std::pow((1 - i), n - idx) * std::pow(i, idx);
                y += control_point.y * std::pow((1 - i), n - idx) * std::pow(i, idx);
            }
            idx++;
        }
        bezier_points.push_back(Point(x,y));
    }
    bezier_points.push_back(control_points[n]);
    return bezier_points;
}

Point TilemapView::findPoint(QString point_id)
{
    for(const Point& point : point_set) {
        if(point.id == point_id) {
            return point;
        }
    }
    return Point(0,0);
}

void TilemapView::setTrack(const QJsonArray& point_array, int div, const QJsonArray& idx_array)
{
    track.clear();
    now_idx_array.clear();
    for(const QJsonValue& i : point_array) {
        track.push_back(Point(i[0].toDouble()/div,i[1].toDouble()/div));
    }
    for(const QJsonValue& i : idx_array) {
        now_idx_array.push_back(i.toInt());
    }
}

void TilemapView::clear()
{
    is_connect = false;
    QPixmap map(VIEW_SIZE,VIEW_SIZE);
    map.fill(QColor(240,240,240));
    this->setPixmap(map);
}

void TilemapView::setPointCloud(point_t * data, int size)
{
    pointcloud.clear();
    for(int i=0;i<size;i++){
        pointcloud.push_back(Point(data->x,data->y));
        data++;
    }
    renderPointcloud();
}

void TilemapView::renderPointcloud()
{
    QPixmap pointcloud_map(VIEW_SIZE, VIEW_SIZE);
    pointcloud_map.fill(Qt::transparent);
    QPainter p(&pointcloud_map);
    p.setPen(QPen(QColor(255,0,0),3));
    for(int i=0;i<pointcloud.size();i++){
        p.drawPoint(xCoordinateToPixel(pointcloud[i].x),yCoordinateToPixel(pointcloud[i].y));
    }
    layer1->setPixmap(pointcloud_map);
}
